
package rendi456.structure.list;

import java.lang.reflect.Field;
import java.lang.reflect.TypeVariable;

public class Liste<E>
{
	private NoeudListe<E> tete;

	public Liste()
	{
		this.tete = null;
	}

	/**
	 * @return vrai si la liste est vide
	 */
	public boolean estVide()
	{
		return this.tete == null;
	}

	/**
	 * vide la liste des valeurs
	 */
	public void vider()
	{
		this.tete = null;
	}

	public void ajouterDebut(E obj)
	{
		NoeudListe<E> nouveau = new NoeudListe<E>(obj, this.tete);
		this.tete = nouveau;
	}

	public void ajouterFin(E obj)
	{
		NoeudListe<E> teteTemp = this.tete;
		NoeudListe<E> noeudFin = null;
		NoeudListe<E> nouveauNoeud = new NoeudListe<E>(obj);

		while (teteTemp != null)
		{
			noeudFin = teteTemp;
			teteTemp = teteTemp.getSuivant();
		}

		if (noeudFin == null)
		{
			this.tete = nouveauNoeud;
		}
		else
		{
			noeudFin.setSuivant(nouveauNoeud);
		}
	}

	/**
	 * Insère un élément à l’intérieur de la liste à l'indice <b>pos</b>
	 * 
	 * @param obj Objet à ajouter
	 * @param pos indice où ajouter l'élément, 0 étant le premier élément de la
	 *            liste.
	 * @throws IndexOutOfBoundsException si pos est hors limite
	 */
	public void ajouterElement(E obj, int pos)
	{
	}

	/**
	 * Insère un élément à l’intérieur de la liste selon un ordre prédéfini. Les
	 * éléments de la liste doivent implémenter l'interface <b>Comparable</b>
	 * i.e. que cette méthode utilise la <b>public int compareTo(Object o)</b>
	 * pour mettre les éléments en ordre.
	 * 
	 * @param obj Objet à ajouter
	 */
	public void ajouterEnOrdre(E obj)
	{
	}

	/**
	 * Retourne l'élément de la liste à l'indice. 0 étant la première position
	 * 
	 * @param pos l'indice de l'élément que l'on veut récupérer dans a liste
	 * 
	 * @return la valeur de l’élément à la position donnée
	 * @throws RuntimeException si l'indice reçu est hors limite
	 */
	public E getElement(int pos) throws RuntimeException
	{
		E out = null;
		NoeudListe<E> courant = null;
		int compteur = 0;
		boolean continuer = true;

		courant = this.tete;

		while (continuer && courant != null)
		{
			if (compteur == pos)
			{
				continuer = false;
				out = courant.getElement();
			}

			compteur++;
			courant = courant.getSuivant();
		}

		if (continuer)
		{
			throw new ListeException();
		}

		return out;
	}

	/**
	 * Rechercher le noeud = à "obj" et retourne la position dans la liste.
	 * 
	 * @param obj Object recherché
	 * @return la position, si l'objet est trouvé ou 0 si l’objet n’est pas
	 *         trouvé. 1 étant la première position.
	 * 
	 */
	public int getPosition(E obj)
	{
		int position = -1;
		// Boucle de recherche
		return position;
	}

	/**
	 * Donne le nombre nombre d'éléments dans la liste.
	 * 
	 * @return le nombre d’éléments de la liste
	 */
	public int getTaille()
	{
		NoeudListe<E> teteTemp = this.tete;
		int taille = 0;

		while (teteTemp != null)
		{
			taille++;
			teteTemp = teteTemp.getSuivant();
		}

		return taille;
	}

	/**
	 * modifie l’élément à la position donnée
	 * 
	 * @param obj ne nouvelle Objet
	 * 
	 * @param pos la position, 0 étant la première position de la iste
	 * 
	 */
	public boolean setElement(E element, int index) throws RuntimeException
	{
		NoeudListe<E> courant = null;
		NoeudListe<E> precedent = null;
		NoeudListe<E> nouveau = null;
		boolean ok = index >= 0;
		int compteur = 0;

		if (ok)
		{
			nouveau = new NoeudListe<E>(element);
			if (this.estVide())
			{
				this.tete = nouveau;
			}
			else
			{
				courant = this.tete;

				do
				{
					precedent = courant;
					courant = courant.getSuivant();

					compteur++;
				}
				while (courant != null && compteur < index);

				precedent.setSuivant(nouveau);
				nouveau.setSuivant(courant);

			}
		}

		return ok;
	}

	/**
	 * Supprime et retourne l’élément au début de la liste
	 * 
	 * @return l'objet retiré dans la liste
	 * 
	 * @exception si la liste est vide @RuntimeException
	 */
	public E enleverDebut()
	{
		E retour = null;

		if (this.tete == null)
		{
			throw new ListeException();
		}
		else
		{
			retour = this.tete.getElement();
			this.tete = this.tete.getSuivant();
		}

		return retour;
	}

	/**
	 * Supprime et retourne l’élément à la fin de la liste
	 * 
	 * @return l'objet retiré dans la liste
	 * 
	 * @exception si la liste est vide @RuntimeException
	 */
	public E enleverFin()
	{
		NoeudListe<E> noeudCourant = null;
		NoeudListe<E> noeudFin = null;
		NoeudListe<E> noeudAvantFin = null;
		E retour = null;

		if (!this.estVide())
		{
			noeudCourant = this.tete;

			if (noeudCourant.getSuivant() != null)
			{
				do
				{
					noeudAvantFin = noeudFin;
					noeudFin = noeudCourant;
					noeudCourant = noeudCourant.getSuivant();
				}
				while (noeudCourant != null);
				noeudAvantFin.setSuivant(null);
			}
			else
			{
				noeudFin = noeudCourant;
				this.tete = null;
			}

			retour = noeudFin.getElement();

		}
		else
		{
			throw new ListeException();
		}

		return retour;
	}

	/**
	 * Supprime et retourne l’élément de position pos
	 * 
	 * @param pos indice de l'élément retiré
	 * @return L'objet retiré dans la liste
	 */
	public E enleverElement(int index)
	{
		NoeudListe<E> courant = null;
		NoeudListe<E> precedent = null;
		NoeudListe<E> suivant = null;
		E retour = null;
		int taille = this.getTaille();

		if (taille < 0 || index >= taille)
		{
			throw new ListeException();
		}
		else
		{
			courant = this.tete;

			for (int i = 0; i < index; i++)
			{
				precedent = courant;
				courant = courant.getSuivant();
			}

			suivant = courant.getSuivant();
			retour = courant.getElement();

			if (precedent != null)
			{
				retour = courant.getElement();
				precedent.setSuivant(suivant);
			}
			else
			{
				this.tete = suivant;
			}
		}

		return retour;
	}

	/**
	 * Supprime le noeud dont l’élément est égal à obj. Pour l'objet, la méthode
	 * <b>boolean equals(Object obj)</b> doit être défini puisque cette méthode
	 * utilise celle-ci pour déterminer l'égalité entre 2 objets.
	 * 
	 * @param obj Objet recherché dans la liste.
	 * @return boolean vrai si l'objet a été retiré de la liste.
	 */
	public boolean enleverElement(E element)
	{
		NoeudListe<E> courant = this.tete;
		NoeudListe<E> precedent = null;
		NoeudListe<E> suivant = null;
		E buffer = null;
		boolean egaliter = false;

		while (!(egaliter || courant == null))
		{
			buffer = courant.getElement();
			suivant = courant.getSuivant();

			egaliter = ((element == null) && (buffer == null))
					|| (element != null && element.equals(buffer));

			if (egaliter)
			{
				if (precedent == null && suivant == null)
				{
					this.tete = null;
				}
				else if (precedent == null)
				{
					this.tete = suivant;
				}
				else if (suivant == null)
				{
					precedent.setSuivant(null);
				}
				else
				{
					precedent.setSuivant(suivant);
				}
			}
			else
			{
				precedent = courant;
				courant = suivant;
			}
		}

		return egaliter;
	}

	/**
	 * @return la liste sous forme de tableau
	 */
	public Object[] toTableau()
	{
		NoeudListe<E> courant = this.tete;
		Object[] tableau = new Object[this.getTaille()];

		for (int i = 0; i < tableau.length; i++)
		{
			tableau[i] = courant.getElement();
			courant = courant.getSuivant();
		}

		return tableau;
	}

	/**
	 * Redéfinit la méthode toString pour retourner le contenu de la liste sous
	 * forme de chaîne de caractères. Appelle successivement la méthode
	 * toString() de tous les éléments de la liste.
	 * 
	 * @return la chaine qui représente le contenu de la liste
	 */
	@Override
	public String toString()
	{
		String out = "";
		NoeudListe<E> courant = this.tete;
		E buffer = null;

		if (!this.estVide())
		{
			while (courant != null)
			{
				buffer = courant.getElement();

				if (buffer == null)
				{
					out += "null";
				}
				else
				{
					out += buffer.toString();
				}
				out += ", ";
				courant = courant.getSuivant();
			}

			out = out.trim();
			out = out.substring(0, out.length() - 1);
		}

		return out;
	}
}
